#include  "common.h"
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define REG 1		//注册
#define CHAT 2		//聊天
#define HEART 3		//保活
#define TRANS 4		//发文件
typedef struct data{	//客户端发送结构体
	char type;
	char fromname[10];
	char toname[10];
	char chat_data[1024];
	struct sockaddr_in addr;
}SEND_MSG;
typedef struct recv{	//客户端接收结构体
	char flag;
	int num;
	char namelist[10][10];
	char fromname[10];
	char chat_data[1024];
	struct sockaddr_in addr;
}RECV_MSG;

//表驱动法，结构体，结构体数组
typedef struct{
	char flag;
	void (*pfun)(RECV_MSG *recv_buf_p, int sockfd);
}EVENT_DRIVE;

void func2(RECV_MSG *recv_buf_p, int sockfd);
void func3(RECV_MSG *recv_buf_p, int sockfd);
void func4(RECV_MSG *recv_buf_p, int sockfd);
void func5(RECV_MSG *recv_buf_p, int sockfd);
void func6(RECV_MSG *recv_buf_p, int sockfd);
void func7(RECV_MSG *recv_buf_p, int sockfd);
void func8(RECV_MSG *recv_buf_p, int sockfd);
void func9(RECV_MSG *recv_buf_p, int sockfd);
const EVENT_DRIVE driveTable[] = {{2, func2}, {3, func3}, {4, func4}, {5, func5}, {6, func6}, {7, func7}, {8, func8}, {9, func9}};


//保活线程
void* heart_beat(void* arg)
{
	int sock = (int )arg;
    SEND_MSG send_buf;
	bzero(&send_buf, sizeof(send_buf));
	send_buf.type = HEART;
    while(1)
	{
		send(sock,&send,sizeof(send),0);
		sleep(600);
	}
	return NULL;
}


//发送文件时，发送端构建tcp客户端
void* trans_file_client(void *arg)
{
	int sockfd;
	sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd < 0)
		err_sys("socket error");
	
	struct sockaddr_in * dst_addr = (struct sockaddr_in *)arg;

	//发起connect操作
	int ret;
	ret = connect(sockfd, (struct sockaddr *)dst_addr, sizeof(struct sockaddr_in));
	if(ret != 0)
		err_sys("connect error");

	int fd;

	printf("input file name\n");
	char buff[1024] = "";
	fgets(buff, sizeof(buff), stdin);
	buff[strlen(buff)-1] = '\0';
	fd = open(buff,O_RDONLY);
	if(fd < 0)
		printf("no such file");
	send(sockfd, buff, strlen(buff), 0);
	recv(sockfd, buff, sizeof(buff), 0);
		
	
	int ret1;
	ret1 = read(fd, buff, sizeof(buff));

	while(ret1 > 0)
	{
		send(sockfd,buff,ret1,0);
		bzero(buff,sizeof(buff));
		ret1 = read(fd,buff,sizeof(buff));
	}
	close(fd);
	
	free(dst_addr);
	close(sockfd);
	return NULL;
}

//发送文件时，接收端构建tcp服务器
void* trans_file_server(void *arg)
{
	//创建套接字
	
	int sockfd;
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd < 0)
		err_sys("socket error");

	printf("trans_file_server\n");
	struct sockaddr_in * recv_addr = (struct sockaddr_in *)arg;

	//指定绑定的地址结构
	struct sockaddr_in my_addr;
	bzero(&my_addr, sizeof(my_addr));
	my_addr.sin_family = AF_INET;
	my_addr.sin_port = htons(8080);
	my_addr.sin_addr.s_addr = htonl(INADDR_ANY);//绑定到本地的任意一个地址

	//绑定本址IP地址与端口
	int ret;
	
	//在bind之前，设置SO_REUSEADDR选项
	int sockopt = 1;
	if((setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt))) < 0)
		err_sys("setsockopt error");	
	
	//绑定
	ret = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));
	if(ret != 0)
		err_sys("bind error");
	memcpy(recv_addr,&my_addr,sizeof(my_addr));
	//开始监听
	ret = listen(sockfd, 5);
    if(ret != 0)
		err_sys("listen error");
    printf("listening ...\n");

	struct sockaddr_in client_addr;
	char client_ip[INET_ADDRSTRLEN] = {0};
	socklen_t cliaddr_len = sizeof(client_addr);
	int connfd;

	//接受一个连接请求
	connfd = accept(sockfd, (struct sockaddr*)&client_addr, &cliaddr_len);//阻塞
	if(connfd < 0)
		err_sys("accept error");
	
	//打印对端的IP地址和端口号
	inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);
	printf("client ip=%s, port=%d\n", client_ip, ntohs(client_addr.sin_port));

	//循环读取数据，再写回套接字，直到对面关闭连接


	char buff[1024] = "";
	int ret1;
	ret1 = recv(connfd, buff, sizeof(buff), 0);
	int fd;
	fd = open(buff,O_CREAT|O_RDWR|O_TRUNC,0666);
	if(fd < 0)
		err_sys("open error!\n");
	send(connfd, buff, ret1, 0);
	ret1 = recv(connfd, buff, sizeof(buff), 0);
	if(ret > 0)
	{
		while(ret1 > 0)
		{
			write(fd, buff, ret1);
			ret1 = recv(connfd, buff, sizeof(buff), 0);
		}
		printf("get file successed!\n");
	}
	else
		printf("get file fail\n");
	//本地也关闭连接
	close(connfd);
	printf("client closed!\n");
	//关闭监听套接字
	close(sockfd);
	free(recv_addr);
	return 0;
		
}

int main(void)
{
	//套接字定义
	int sockfd;
	sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd < 0)
		err_sys("socket error!\n");
	
	//服务器地址结构
	struct sockaddr_in dst_addr;
	bzero(&dst_addr, sizeof(dst_addr));
	dst_addr.sin_family = AF_INET;
	dst_addr.sin_port = htons(9999);
	inet_pton(AF_INET,"127.0.0.1",&dst_addr.sin_addr.s_addr);

	//connect等待服务器accept
	int ret;
	ret = connect(sockfd, (struct sockaddr *)&dst_addr, sizeof(dst_addr));
	if(ret != 0)
		err_sys("connect error!");
	
	SEND_MSG send_buf;
	RECV_MSG recv_buf;
	bzero(&send_buf,sizeof(send_buf));
	bzero(&recv_buf,sizeof(recv_buf));
	
	//注册名字发送给服务器
	send_buf.type = REG;
	printf("input your name:\n");
//	char buff_name[10] = "";
//	fgets(buff_name,10,stdin);	
//	strncpy(send_buf.fromname, b	uff_name, strlen(buff_name)-1);
	fgets(send_buf.fromname,10,stdin);
	send(sockfd,&send_buf,sizeof(send_buf),0);
		
	//接收服务器回应
	ret = recv(sockfd, &recv_buf, sizeof(recv_buf), 0);

	if(ret == 0)
	{
		printf("server closed!\n");
		return -1;		
	}
	if(recv_buf.flag == 0)
	{
		printf("same name\n");
		return -1;
	}

	//创建保活线程，定时发送保活数据
	pthread_t tid;
	pthread_create(&tid, NULL, heart_beat, (void *)sockfd);
	pthread_detach(tid);

	int i = 0;
	printf("online username: \n");
	for(i = 0; i < recv_buf.num; i++) 
		printf("%s\n",recv_buf.namelist[i]);



	int maxfd = sockfd + 1;
	fd_set set;
	FD_ZERO(&set);
	int stdineof = 0;
	while(1)
	{
		printf("send to :\n");
		if(stdineof == 0)
			FD_SET(0,&set);
		FD_SET(sockfd,&set);
		select(maxfd,&set,NULL,NULL,NULL);
		if(FD_ISSET(0,&set))
		{		
			send_buf.type = CHAT;
			fgets(send_buf.toname,sizeof(send_buf.toname),stdin);
			printf("context:\n");
			fgets(send_buf.chat_data,sizeof(send_buf.chat_data),stdin);
			if(strncmp(send_buf.chat_data, "trans file", strlen("trans file")) == 0)
				send_buf.type = TRANS;
			
			if(strncmp(send_buf.toname,"server",strlen("server")) == 0){
				if(strncmp(send_buf.chat_data,"stop",strlen("stop")) == 0)
				{
					stdineof = 1;
					shutdown(sockfd,SHUT_WR);
					FD_CLR(0,&set);
					continue;
				}
				if(strncmp(send_buf.chat_data, "history", strlen("history")) == 0)
//				{
//					printf("history:\n");
					send(sockfd,&send_buf,sizeof(send_buf),0);
//					char history_buf[1024] = "";
//					int fd = open(send_buf.fromname, O_RDWR|O_CREAT|O_APPEND, 0777);
//					size_t num;
//					num = recv(sockfd, history_buf, sizeof(history_buf)
//					num = read(fd, history_buf, sizeof(history_buf));
//					while(num > 0)
//					{
//						printf("%s", history_buf);
//						bzero(history_buf, sizeof(history_buf));
//						num = read(fd, history_buf, sizeof(history_buf));
//					}
//					close(fd);
//				}
			}
			else
				send(sockfd,&send_buf,sizeof(send_buf),0);
		}
		if(FD_ISSET(sockfd,&set))
		{
			int ret;
			ret = recv(sockfd,&recv_buf,sizeof(recv_buf),0);
			if(ret == 0)
			{
		 		printf("server close!\n");
				break;
			}
			
			int i;
			for(i = 0; i < sizeof(driveTable)/sizeof(EVENT_DRIVE); i++)
			{
				if(driveTable[i].flag == recv_buf.flag)
				{
					driveTable[i].pfun(&recv_buf, sockfd);
					break;
				}
			}
		}
	}
	close(sockfd);
	return 0;
}

void func2(RECV_MSG *recv_buf_p, int sockfd)
{
	char buffname[10] = "";
	strncpy(buffname, recv_buf_p->fromname, strlen(recv_buf_p->fromname)-1);	
	printf("from %s: %s\n",buffname, recv_buf_p->chat_data);
//	printf("%s\n",recv_buf_p->chat_data);	
}
void func3(RECV_MSG *recv_buf_p, int sockfd)
{
	printf("no such name\n");
}
void func4(RECV_MSG *recv_buf_p, int sockfd)
{
	printf("%s\n",recv_buf_p->chat_data);
	SEND_MSG send_buf;
	send_buf.type = TRANS;
	fgets(send_buf.chat_data,sizeof(send_buf.chat_data),stdin);
	strcpy(send_buf.toname, recv_buf_p->fromname);
	if((strncmp(send_buf.chat_data,"y",strlen("y"))) == 0)
	{
		struct sockaddr_in *recv_addr = (struct sockaddr_in*)malloc(sizeof(struct sockaddr_in));
		pthread_t tid;
		pthread_create(&tid, NULL, trans_file_server, (void *)recv_addr);
		pthread_detach(tid);
		usleep(1000);
		memcpy(&send_buf.addr,recv_addr,sizeof(struct sockaddr_in));
	}
	send(sockfd,&send_buf,sizeof(send_buf),0);	 
}
void func5(RECV_MSG *recv_buf_p, int sockfd)
{
	struct sockaddr_in *recv_addr = (struct sockaddr_in*)malloc(sizeof(struct sockaddr_in));
	memcpy(recv_addr, &(recv_buf_p->addr),sizeof(struct sockaddr_in));
	pthread_t tid;
	pthread_create(&tid, NULL, trans_file_client, (void *)recv_addr);
	pthread_detach(tid);		
}
void func6(RECV_MSG *recv_buf_p, int sockfd)
{
	printf("%s\n",recv_buf_p->chat_data);
}
void func7(RECV_MSG *recv_buf_p, int sockfd)
{
	char buffname[10] = "";
	strncpy(buffname, recv_buf_p->fromname, strlen(recv_buf_p->fromname)-1);	
	printf("%s log on\n", buffname);
}
void func8(RECV_MSG *recv_buf_p, int sockfd)
{
	char buffname[10] = "";
	strncpy(buffname, recv_buf_p->fromname, strlen(recv_buf_p->fromname)-1);
	printf("%s log off\n", buffname);
}
void func9(RECV_MSG *recv_buf_p, int sockfd)
{
	printf("%s", recv_buf_p->chat_data);		
}